using System.Collections.Generic;
using System.Text;
using Framework.DataTable;
using UnityEngine;

namespace Framework
{
    public class TemplateDataTable
    {
        public string contentItem;
        public string contentTable;
        protected Dictionary<string, string> _set = new Dictionary<string, string>();
        protected IDataTableProcessor tableProcessor;
        protected ClassInfo ClassInfo;
        public TemplateDataTable()
        {
        }
        public void Set(string name, string value)
        {
            _set.Add(name, value);
        }
        public string Get(string name)
        {
            _set.TryGetValue(name, out string value);
            return value;
        }
        public void SetDefine(string value)
        {
            Set("define", value);
        }
        public void SetItemClassName(string value)
        {
            Set("item_class_name", value);
        }
        public void SetItemInstanceClassName(string value)
        {
            Set("item_instance_name", value);
        }
        public void SetTableClassName(string value)
        {
            Set("class", value);
        }
        public void SetClassInfo(ClassInfo value)
        {
            ClassInfo = value;
        }
        public void SetParser(IDataTableProcessor tableProcessor)
        {
            this.tableProcessor = tableProcessor;
        }
        public string GenCode()
        {
            var templateItem = new TemplateContent(contentItem);
            templateItem.AddValue("/item_class_name/", Get("item_class_name"));
            foreach (var field in ClassInfo.Fields)
            {
                if (!string.IsNullOrEmpty(field.Comment))
                {
                    templateItem.AddValue("/fields/", "/// <summary>");
                    templateItem.AddValue("/fields/", "///" + field.Comment);
                    templateItem.AddValue("/fields/", "/// </summary>");
                }
                var fieldCode = GenerateField(field);
                templateItem.AddValue("/fields/", fieldCode);
                var parseCode = GenerateParseScript(field.Type, string.Format("values[{0}]", field.DeclarIndex), field.Name);
                if (string.IsNullOrEmpty(parseCode))
                {
                    return string.Empty;
                }
                var encodeCode = GenerateEncodeScript(field.Type, field.Name);
                if (string.IsNullOrEmpty(encodeCode))
                {
                    return string.Empty;
                }
                var decodeCode = GenerateDecodeScript(field.Type, field.Name);
                if (string.IsNullOrEmpty(decodeCode))
                {
                    return string.Empty;
                }
                if (!string.IsNullOrEmpty(parseCode))
                {
                    templateItem.AddValue("/parse/", parseCode + (field.Type.IsArray ? "" : ";"));
                }
                if (!string.IsNullOrEmpty(encodeCode))
                {
                    templateItem.AddValue("/encode/", encodeCode + (field.Type.IsArray ? "" : ";"));
                }
                if (!string.IsNullOrEmpty(decodeCode))
                {
                    templateItem.AddValue("/decode/", decodeCode + (field.Type.IsArray ? "" : ";"));
                }
            }
            var branchSb = new StringBuilder();
            var blen = ClassInfo.Branchs.Length;
            for (int i = 0; i < blen; i++)
            {
                if (i > 0)
                {
                    branchSb.Append(", ");
                }
                branchSb.Append("\"" + ClassInfo.Branchs[i] + "\"");
            }
            var bset = "_branchs = new List<string>() { " + branchSb.ToString() + " }";

            var templateTable = new TemplateContent(contentTable);
            templateTable.AddValue("/class/", Get("class"));
            templateTable.AddValue("/item_class_name/", Get("item_class_name"));
            templateTable.AddValue("/name/", "\"" + ClassInfo.Name + "\"");
            templateTable.AddValue("/item_instance_name/", string.IsNullOrEmpty(Get("item_instance_name")) ? Get("item_class_name") : Get("item_instance_name"));
            templateTable.AddValue("/branch/", bset);
            templateTable.AddValue("/index_define/", ClassInfo.Index != null ? "true" : "false");
            templateTable.AddValue("/index_type/", ClassInfo.Index.Type.TypeName);
            templateTable.AddValue("/index_name/", ClassInfo.Index.Name);

            return templateTable.Process() + "\n" + templateItem.Process();
        }
        private string GenerateField(FieldInfo info)
        {
            var type = info.Type;
            var fieldName = info.Name;
            var fieldTypeName = tableProcessor.GetFieldType(type.TypeName);
            if (type.IsArray)
            {
                var gtype = new SimpleTypeDef(fieldTypeName);
                if (gtype.IsClass)
                {
                    if (tableProcessor.GetParser(fieldTypeName) == null)
                    {
                        return string.Format("//public {0}[] {1} ", fieldTypeName, fieldName) + "{ get; protected set; }";
                    }
                }
                return string.Format("public {0}[] {1} ", fieldTypeName, fieldName) + "{ get; protected set; }";
            }
            if (type.IsClass && tableProcessor.GetParser(fieldTypeName) == null)
            {
                return string.Format("//public {0} {1} ", fieldTypeName, fieldName) + "{ get; protected set; }";
            }
            return string.Format("public {0} {1} ", fieldTypeName, fieldName) + "{ get; protected set; }";
        }
        private string GenerateParseScript(SimpleTypeDef type, string value, string fieldName = "")
        {
            string filedDeclare = string.Empty;
            if (!string.IsNullOrEmpty(fieldName))
            {
                filedDeclare = fieldName + " = ";
            }
            var fieldTypeName = tableProcessor.GetFieldType(type.TypeName);
            if (type.IsArray)
            {
                var itemT = fieldTypeName;
                var tmpListName = "s_" + fieldName;

                var itemTpl = GenerateParseScript(new SimpleTypeDef(itemT), tmpListName + "[i]");
                if (!string.IsNullOrEmpty(itemTpl))
                {
                    var c = "if (Framework.Utils.Parse.ParseList(values) is string[] slist) { namedef = new typedef[slist.Length]; for (int i = 0; i < slist.Length; i++) { namedef[i] = parse; } }";
                    var template = new TemplateContent(c);
                    template.AddValue("values", value);
                    template.AddValue("slist", tmpListName);
                    template.AddValue("namedef", fieldName);
                    template.AddValue("typedef", itemT);
                    template.AddValue("parse", itemTpl);
                    return template.Process();
                }
                return string.Empty;
            }
            if (type.IsString)
            {
                return string.Format("{0}{1}", filedDeclare, value);
            }
            var cfg = tableProcessor.GetParser(fieldTypeName);
            if (cfg != null)
            {
                return string.Format("{0}" + cfg.ParseScript + "({1})", filedDeclare, value);
            }
            if (type.IsValueType)
            {
                var name = Utils.Text.FirstCharUpper(fieldTypeName);
                return string.Format("{0}Framework.Utils.Parse.Parse{1}({2})", filedDeclare, name, value);
            }
            if (type.IsEnum)
            {
                return string.Format("{0}({2})Framework.Utils.Parse.ParseInt({1})", filedDeclare, value, fieldTypeName);
            }
            UnityEngine.Debug.LogError("没有找到对应的解析:" + fieldTypeName + " " + fieldName);
            return string.Empty;
        }

        private string GenerateEncodeScript(SimpleTypeDef type, string fieldName)
        {
            var fieldTypeName = tableProcessor.GetFieldType(type.TypeName);
            if (type.IsArray)
            {
                var itemT = fieldTypeName;
                var itemSource = fieldName + "[i]";
                var itemTpl = GenerateEncodeScript(new SimpleTypeDef(itemT), itemSource);
                if (!string.IsNullOrEmpty(itemTpl))
                {
                    var c = "if (namedef != null) { buffer.Write((ushort)namedef.Length); for (var i = 0; i < (ushort)namedef.Length; i++) { encode; } } else { buffer.Write((ushort)0); }";
                    var template = new TemplateContent(c);
                    template.AddValue("namedef", fieldName);
                    template.AddValue("encode", itemTpl);
                    return template.Process();
                }
                return string.Empty;
            }
            var cfg = tableProcessor.GetParser(fieldTypeName);
            if (cfg != null)
            {
                return string.Format(cfg.EncodeScript + "({0}, {1})", fieldName, "buffer");
            }
            if (type.IsString)
            {
                return string.Format("buffer.Write({0})", fieldName);
            }
            if (type.IsValueType)
            {
                return string.Format("buffer.Write({0})", fieldName);
            }
            if (type.IsEnum)
            {
                return string.Format("buffer.Write((int){0})", fieldName);
            }
            UnityEngine.Debug.LogError("没有找到对应的序列化:" + fieldTypeName + " " + fieldName);
            return string.Empty;
        }
        private string GenerateDecodeScript(SimpleTypeDef type, string fieldName = "")
        {
            var fieldTypeName = tableProcessor.GetFieldType(type.TypeName);
            string filedDeclare = string.Empty;
            if (!string.IsNullOrEmpty(fieldName))
            {
                filedDeclare = fieldName + " = ";
            }
            if (type.IsArray)
            {
                var itemT = fieldTypeName;
                var itemTpl = GenerateDecodeScript(new SimpleTypeDef(itemT), string.Empty);
                if (!string.IsNullOrEmpty(itemTpl))
                {
                    var c = "namedef = new typedef[buffer.ReadUshort()]; for (var i = 0; i < namedef.Length; i++) { namedef[i] = decode; }";
                    var template = new TemplateContent(c);
                    template.AddValue("namedef", fieldName);
                    template.AddValue("typedef", fieldTypeName);
                    template.AddValue("decode", itemTpl);
                    return template.Process();
                }
                return string.Empty;
            }

            var cfg = tableProcessor.GetParser(fieldTypeName);
            if (cfg != null)
            {
                return string.Format(filedDeclare + cfg.DecodeScript + "({0})", "buffer");
            }
            if (type.IsString)
            {
                return string.Format("{0}buffer.ReadString()", filedDeclare);
            }
            if (type.IsValueType)
            {
                var name = Utils.Text.FirstCharUpper(fieldTypeName);
                return string.Format("{0}buffer.Read{1}()", filedDeclare, name);
            }
            if (type.IsEnum)
            {
                return string.Format("{0}({1})buffer.ReadInt()", filedDeclare, fieldTypeName);
            }
            UnityEngine.Debug.LogError("没有找到对应的反序列化:" + fieldTypeName + " " + fieldName);
            return string.Empty;
        }

    }
    public class TemplateDataTableManager
    {
        public string contentManager;
        protected Dictionary<string, string> _set = new Dictionary<string, string>();
        private string className;
        public TemplateDataTableManager()
        {

        }
        public void SetClassName(string value)
        {
            className = value;
        }
        public void AddItem(string name, string typeName)
        {
            _set.Add(name, typeName);
        }
        public string Process()
        {
            var content = new TemplateContent(contentManager);
            content.AddValue("/class/", this.className);
            var lst = new List<string>();
            foreach (var item in _set)
            {
                lst.Add(item.Key);
            }
            lst.Sort();
            foreach (var item in lst)
            {
                var t = _set[item];
                content.AddValue("/fields/", string.Format("public {0} {1}", t, item) + " { get; private set; }");
                content.AddValue("/parse/", string.Format("{1} = add(new {0}());", t, item));
            }
            return content.Process();
        }
    }
    public class TemplateContent
    {
        private static StringBuilder builder = new StringBuilder();
        private string _content;
        private Dictionary<string, List<string>> _datas;
        public TemplateContent(string content)
        {
            _content = content;
            _datas = new Dictionary<string, List<string>>();
        }
        public void AddValue(string name, string value)
        {
            if (!_datas.TryGetValue(name, out var list))
            {
                list = new List<string>();
                _datas.Add(name, list);
            }
            list.Add(value);
        }
        public string Process()
        {
            var values = new List<string>();
            var datas = new List<ReplaceData>();
            var lines = _content.Split("\n");
            var lineLen = lines.Length;
            var keys = new List<string>();
            for (int i = 0; i < lineLen; i++)
            {
                var line = lines[i];
                int c = line.Length;
                foreach (var item in _datas.Keys)
                {
                    keys.Add(item);
                }
                while (true && c-- > 0)
                {
                    int matchId = int.MaxValue;
                    string matchItem = string.Empty;
                    var keyLen = keys.Count;
                    while (keyLen-- > 0)
                    {
                        var item = keys[keyLen];
                        var id = line.IndexOf(item);
                        if (id < 0)
                        {
                            keys.RemoveAt(keyLen);
                            continue;
                        }
                        if (id < matchId)
                        {
                            matchId = id;
                            matchItem = item;
                        }
                    }
                    if (!string.IsNullOrEmpty(matchItem))
                    {
                        var d = new ReplaceData();
                        datas.Add(d);
                        d.Name = matchItem;
                        var perfix = line.Substring(0, matchId);
                        if (string.IsNullOrEmpty(perfix.Trim()))
                        {
                            d.prefix = perfix;
                        }
                        else
                        {
                            values.Add(perfix);
                        }
                        d.Index = values.Count;
                        values.Add(matchItem);
                        line = line.Substring(matchId + matchItem.Length);
                    }
                    else
                    {
                        break;
                    }
                }
                if (!string.IsNullOrEmpty(line))
                {
                    values.Add(line);
                }
                if (i < lineLen - 1)
                {
                    values.Add("\n");
                }
            }

            foreach (var item in datas)
            {
                if (_datas.TryGetValue(item.Name, out var list))
                {
                    if (list.Count < 1)
                    {
                        continue;
                    }
                    foreach (var v in list)
                    {
                        item.AddLine(v);
                    }
                }
                values[item.Index] = item.Context;
            }
            builder.Clear();
            foreach (var item in values)
            {
                builder.Append(item);
            }
            return builder.ToString();
        }

    }
    public class ReplaceData
    {
        public string Name;
        public int Index;
        public string prefix;
        public string Context;
        public ReplaceData()
        {
            prefix = string.Empty;
            Index = 0;
            Context = string.Empty;
        }
        public void AddLine(string value)
        {
            if (string.IsNullOrEmpty(Context))
            {
                Context += prefix + value;
            }
            else
            {
                Context += "\n" + prefix + value;
            }
        }
    }
}